#include <stdio.h>
#include <stdint.h>

// 工厂方法：创建LCD实例
LCD_HandleTypeDef LCD_Create(LCD_ConfigTypeDef* config)
{
    return (LCD_HandleTypeDef)config;
}

// 基础写操作
static void lcd_write(LCD_HandleTypeDef hlcd, uint8_t data, uint8_t rs_mode)
{
    LCD_ConfigTypeDef* lcd = (LCD_ConfigTypeDef*)hlcd;
    
    // 设置RS引脚
    rs_mode ? lcd->RS.set_high() : lcd->RS.set_low();
    
    // 设置RW为写模式
    lcd->RW.set_low();

    // 处理数据线
    if(lcd->data_bits == 8) {
        // 8位模式直接写入
        for(int i=0; i<8; i++) {
            (data & (1<<i)) ? lcd->DATA[i].set_high() : lcd->DATA[i].set_low();
        }
    } else {
        // 4位模式分两次传输
        // 先传高四位
        for(int i=4; i<8; i++) {
            (data & (1<<i)) ? lcd->DATA[i-4].set_high() : lcd->DATA[i-4].set_low();
        }
        lcd->EN.set_high();
        lcd->delay_us(1);
        lcd->EN.set_low();
        
        // 再传低四位
        for(int i=0; i<4; i++) {
            (data & (1<<i)) ? lcd->DATA[i].set_high() : lcd->DATA[i].set_low();
        }
    }

    // 产生EN脉冲
    lcd->EN.set_high();
    lcd->delay_us(1);
    lcd->EN.set_low();
    
    lcd->delay_us(100);  // 保持时间
}

// 初始化流程
void LCD_Init(LCD_HandleTypeDef hlcd)
{
    LCD_ConfigTypeDef* lcd = (LCD_ConfigTypeDef*)hlcd;
    
    // 硬件初始化
    for(int i=0; i<lcd->data_bits; i++) {
        lcd->DATA[i].set_output();
    }
    lcd->RS.set_output();
    lcd->RW.set_output();
    lcd->EN.set_output();

    // 初始化序列
    lcd->delay_ms(50);
    lcd_write(hlcd, LCD_CMD_FUNCTION_SET | 0x30, 0);
    lcd->delay_ms(5);
    lcd_write(hlcd, LCD_CMD_FUNCTION_SET | 0x30, 0);
    lcd->delay_us(100);
    lcd_write(hlcd, LCD_CMD_FUNCTION_SET | 0x30, 0);
    
    // 设置工作模式
    uint8_t function_set = LCD_CMD_FUNCTION_SET;
    function_set |= (lcd->data_bits == 8) ? 0x10 : 0x00;
    lcd_write(hlcd, function_set, 0);
    
    // 显示设置
    lcd_write(hlcd, LCD_CMD_DISPLAY_CTRL | 0x0C, 0); // 显示开，无光标
    lcd_write(hlcd, LCD_CMD_ENTRY_MODE | 0x02, 0);   // 地址自动递增
    LCD_Clear(hlcd);
}

// 高级API
void LCD_Clear(LCD_HandleTypeDef hlcd)
{
    lcd_write(hlcd, LCD_CMD_CLEAR, 0);
    ((LCD_ConfigTypeDef*)hlcd)->delay_ms(2);
}

void LCD_SetCursor(LCD_HandleTypeDef hlcd, uint8_t row, uint8_t col)
{
    const uint8_t row_offsets[] = {0x00, 0x40};
    lcd_write(hlcd, LCD_CMD_DDRAM_ADDR | (col + row_offsets[row]), 0);
}

void LCD_Print(LCD_HandleTypeDef hlcd, const char* str)
{
    while(*str) {
        lcd_write(hlcd, *str++, 1);
    }
}